package com.yricky.astral.signal

import com.yricky.astral.lib.VerilogKeyword.ALWAYS
import com.yricky.astral.lib.VerilogKeyword.BEGIN
import com.yricky.astral.lib.VerilogKeyword.ELSE
import com.yricky.astral.lib.VerilogKeyword.END
import com.yricky.astral.lib.VerilogKeyword.IF
import com.yricky.astral.lib.VerilogKeyword.NEGEDGE
import com.yricky.astral.lib.VerilogKeyword.POSEDGE
import com.yricky.astral.project.Hazuki
import com.yricky.astral.structure.INetList
import com.yricky.astral.structure.AstralModule
import com.yricky.astral.structure.ClkRstPair

/**
 * Don't use this directly!
 * @see AstralModule.reg
 */
class Reg(override val name: String, override val width: Int, override val netList: INetList, private val clkRstPair: ClkRstPair?)
    :AssignableSignal,Hazuki.Checkable by netList {
    init {
        if(width >64){
            standard.check(Hazuki.Rules.WidthTooLarge,"Width of reg signal $name is $width",Hazuki.CheckLevel.allow)
        }
    }

    override fun level(): Int {
//        return sourceSignal?.let {
//            if(isTemporal){
//                if((clkRstPair != netList.clkRstPair) or (it.level()<0))
//                    -1
//                else
//                    it.level() + 1
//            }else{ it.level() }
//        } ?:
        return 0
    }

    var isTemporal:Boolean = clkRstPair != null

    private val trigSignal:Signal? get() = clkRstPair?.clock

    override var sourceSignal:Signal? = null


    override val tags: MutableSet<String> by lazy {
        HashSet<String>().also { it.add(Signal.TAG_REG) }
    }

    override fun generateAssignTo():String{
        return sourceSignal?.generateBody() ?: ""
    }

    override fun generateLogic(): String {
        return if(isTemporal) {
            "$ALWAYS@($POSEDGE ${trigSignal!!.generateBody()} or $NEGEDGE ${clkRstPair!!.reset.name})$BEGIN\n" +
            "  $IF(!${clkRstPair.reset.name})$BEGIN\n" +
            "    $name <= $width'b0;\n" +
            "  $END \n" +
            "  $ELSE $BEGIN\n" +
            (if(sourceSignal!=null)
            "    $name <= ${generateAssignTo()};\n"
            else "") +
            "  $END\n" +
            "$END\n"
        }else if(sourceSignal != null) {
            "$ALWAYS@(*)$BEGIN\n" +
            "  $name = ${generateAssignTo()};\n" +
            "$END\n"
        } else { "" }
    }

    override fun generateDeclare(): String {
        val widthInfo = if(width>1) "[${width-1}:0]" else ""
        return "reg$widthInfo $name;\n"
    }
}